/*
 * Copyright (c) 2010 MoSync AB
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "PlayScreen.h"

#include <MAInterpreter/EasyTextDownloader.h>
#include <MAInterpreter/System.h>

#include <MAUI/Widget.h>
#include <MAUI/Layout.h>
#include <MAUI/Label.h>

#include <maapi.h>
#include <conprint.h>

using namespace std;
using namespace MAUI;
using namespace MAUtil;

// ====== Global variables visible in this file ======

static int ScreenWidth = Sys()->getScreenWidth();
static int ScreenHeight = Sys()->getScreenHeight();
static int ButtonBarHeight = 30;
static int PlayfieldHeight = ScreenHeight - ButtonBarHeight;
static int ButtonPadding = 6;
static int TextPadding = 3;

static int ButtonColor1 = 0x00CC00;
static int ButtonColor2 = 0x00AA00;

static Font* TextFont;
static Font* ButtonFont;

static char* ReloadButtonText = "Reload";
static char* EvalButtonText = "Eval";
static char* BackButtonText = "Back";
static char* LoadingScript = "Loading script";
static char* ScriptCouldNotBeLoaded = "Script could not be loaded";

// ====== Functions visible in this file ======

// TODO: Move this stuff to common super class for screen.
//
static void setButtonPadding(Widget *w) {
	w->setPaddingLeft(ButtonPadding);
	w->setPaddingBottom(ButtonPadding);
	w->setPaddingRight(ButtonPadding);
	w->setPaddingTop(ButtonPadding);
}

static void setTextPadding(Widget *w) {
	w->setPaddingLeft(TextPadding);
	w->setPaddingBottom(TextPadding);
	w->setPaddingRight(TextPadding);
	w->setPaddingTop(TextPadding);
}

static Layout* createMainLayout()
{
	Layout* layout = new Layout(
		0,
		0,
		ScreenWidth,
		ScreenHeight,
		NULL,
		1,
		2);
	return layout;
}

static Widget* createPlayfield()
{
	Label* label = new Label(
		0,
		0,
		ScreenWidth,
		PlayfieldHeight,
		NULL,
		"",
		0x000000,
		TextFont);
	label->setMultiLine(true);
	setTextPadding(label);

	return label;
}

static Widget* createButtonBar()
{
	int numberOfButtons = 3;

	Layout* layout = new Layout(
		0,
		0,
		ScreenWidth,
		ButtonBarHeight,
		NULL,
		numberOfButtons,
		1);

	Label* backButton = new Label(
		0,
		0,
		ScreenWidth / numberOfButtons,
		ButtonBarHeight,
		layout,
		BackButtonText,
		ButtonColor1,
		ButtonFont);
	backButton->setHorizontalAlignment(Label::HA_RIGHT);
	setButtonPadding(backButton);

	Label* reloadButton = new Label(
		0,
		0,
		ScreenWidth / numberOfButtons,
		ButtonBarHeight,
		layout,
		ReloadButtonText,
		ButtonColor2,
		ButtonFont);
	reloadButton->setHorizontalAlignment(Label::HA_CENTER);
	setButtonPadding(reloadButton);

	Label* evalButton = new Label(
		0,
		0,
		ScreenWidth / numberOfButtons,
		ButtonBarHeight,
		layout,
		EvalButtonText,
		ButtonColor1,
		ButtonFont);
	evalButton->setHorizontalAlignment(Label::HA_LEFT);
	setButtonPadding(evalButton);

	return layout;
}

static Widget* getBackButton(Widget* buttonBar)
{
	return buttonBar->getChildren()[0];
}

static Widget* getReloadButton(Widget* buttonBar)
{
	return buttonBar->getChildren()[1];
}

static Widget* getEvalButton(Widget* buttonBar)
{
	return buttonBar->getChildren()[2];
}

static char* extractLuaScript(char* data)
{
	lprintfln("extractLuaScript");
	int i = 100;
	char c = data[i];
	data[i] = 0;
	lprintfln("extractLuaScript data: %s", data);
	data[i] = c;

	// Extract data between tags MOBILELUA_BEGIN and MOBILELUA_END,
	// if they are present.
	char* startTag = "MOBILELUA_BEGIN";
	char* endTag = "MOBILELUA_END";

	char* start = strstr(data, startTag);
	char* end = strstr(data, endTag);

	// TODO: We should do some error reporting.
	// Maybe an error screen? Use also for syntax/runtime errors.
	if (NULL != start && NULL != end && (int)end > (int)start)
	{
		// Return pointer to the script within the tags.
		start = start + strlen(startTag);
		*end = 0;

		lprintfln("extractLuaScript script: %s", start);

		return start;
	}
	else
	{
		lprintfln("extractLuaScript tags not found");
		return data;
	}
}

// ====== Helper classes ======

/**
 * Class that downloads and runs a script.
 */
class DownloadAndRunScript : public EasyTextDownloader
{
private:
	PlayScreen* playScreen;

public:
	DownloadAndRunScript(PlayScreen* playScreen)
	{
		this->playScreen = playScreen;
	}

	void onDownloadComplete(char* data)
	{
		if (NULL != data)
		{
			lprintfln("data is OK");
			char* script = extractLuaScript(data);
			lprintfln("script: %s", script);
			playScreen->runScript(script);
			EasyTextDownloader::freeData(data);
		}
		else
		{
			lprintfln("data is NULL");
		}
	}
};

/**
 * Class that downloads and evaluates a script.
 */
class DownloadAndEvalScript : public EasyTextDownloader
{
private:
	PlayScreen* playScreen;

public:
	DownloadAndEvalScript(PlayScreen* playScreen)
	{
		this->playScreen = playScreen;
	}

	void onDownloadComplete(char* data)
	{
		if (NULL != data)
		{
			playScreen->evalScript(extractLuaScript(data));
			EasyTextDownloader::freeData(data);
		}
	}
};

/*
// ====== Class ScriptRunnerTimer ======

class ScriptRunner : public TimerListener
{
public:
	ScriptRunner(PlayScreen* playScreen)
	{
		this->playScreen = playScreen;
	}

	void runTimerEvent()
	{
		playScreen->runScript();
		delete this;
	}

private:
	PlayScreen* playScreen;
};
*/

// ====== Methods in class PlayScreen ======

PlayScreen::PlayScreen(Screen* previousScreen)
{
	this->previousScreen = previousScreen;

	this->interpreter = NULL;

	// TODO: Make these instance variables.
	TextFont = Sys()->getFont("Arial20White");
	ButtonFont = Sys()->getFont("Arial20BoldWhite");

	playfield = createPlayfield();
	Widget* buttonBar = createButtonBar();

	reloadButton = getReloadButton(buttonBar);
	evalButton = getEvalButton(buttonBar);
	backButton = getBackButton(buttonBar);

	mainLayout = createMainLayout();
	mainLayout->add(playfield);
	mainLayout->add(buttonBar);

	this->setMain(mainLayout);
}

PlayScreen::~PlayScreen()
{
	if (interpreter)
	{
		interpreter->close();
		delete interpreter;
		interpreter = NULL;
	}

	if (getMain())
	{
		delete getMain();
	}
}

Widget* PlayScreen::getPlayfield()
{
	return playfield;
}

void PlayScreen::setInterpreter(AbstractInterpreter* interpreter)
{
	this->interpreter = interpreter;
	interpreter->initialize();

	interpreter->setScreenLeft(0);
	interpreter->setScreenTop(0);
	interpreter->setScreenWidth(ScreenWidth);
	interpreter->setScreenHeight(PlayfieldHeight);

	interpreter->setInterpreterErrorListener(this);
}

void PlayScreen::onInterpreterError(const char* errorMessage)
{
	lprintfln("Interpreter error: %s", errorMessage);
}

void PlayScreen::show()
{
	Screen::show();
}

void PlayScreen::hide()
{
	Screen::hide();
}

void PlayScreen::setScriptURL(const String& url)
{
	scriptURL = url;
}

void PlayScreen::loadAndRun()
{
	// The downloader will delete itself when finished.
	lprintfln("Downloading: %s", scriptURL.c_str());
	EasyTextDownloader* downloader = new DownloadAndRunScript(this);
	downloader->get(scriptURL.c_str());
}

void PlayScreen::loadAndEval()
{
	// The downloader will delete itself when finished.
	EasyTextDownloader* downloader = new DownloadAndEvalScript(this);
	downloader->get(scriptURL.c_str());
}

void PlayScreen::runScript(char* script)
{
	if (interpreter && script)
	{
		interpreter->eval(script);
	}
}

void PlayScreen::evalScript(char* script)
{
	if (interpreter && script)
	{
		interpreter->eval(script);
	}
}

void PlayScreen::goBack()
{
	if (previousScreen)
	{
		lprintfln("Going back");
		this->hide();
		previousScreen->show();
		delete this; // !!
	}
}

void PlayScreen::drawScreen()
{
}

void PlayScreen::keyPressEvent(int keyCode, int nativeCode)
{
	switch(keyCode) {
		case MAK_BACK:
		case MAK_SOFTLEFT:
			goBack();
			break;
		case MAK_FIRE:
			loadAndRun();
			break;
		case MAK_SOFTRIGHT:
			loadAndEval();
			break;
		default:
			lprintfln("Key code: %d", keyCode);
			if (interpreter) { interpreter->onKeyDown(keyCode, nativeCode); }
			break;
	}
}

void PlayScreen::keyReleaseEvent(int keyCode, int nativeCode)
{
	interpreter->onKeyUp(keyCode, nativeCode);
}

void PlayScreen::pointerPressEvent(MAPoint2d point)
{
	Widget* w = mainLayout->widgetAt(point.x, point.y);
	if (w == backButton)
	{
		goBack();
	}
	else if (w == reloadButton)
	{
		loadAndRun();
	}
	else if (w == evalButton)
	{
		loadAndEval();
	}
	else
	{
		if (interpreter) { interpreter->onTouchDown(point.x, point.y, 0); }
	}
}

void PlayScreen::pointerReleaseEvent(MAPoint2d point)
{
	if (interpreter) { interpreter->onTouchUp(point.x, point.y, 0); }
}

void PlayScreen::pointerMoveEvent(MAPoint2d point)
{
	if (interpreter) { interpreter->onTouchDrag(point.x, point.y, 0); }
}
